using System;
using System.Collections.Generic;
using Behaving.Composites;
using Behaving.Decorators;

namespace Behaving
{
    public abstract class Behaviour : INamed
    {
        private List<INotifyOnParentStop> _childrenToNotifyOnStop;

        public string Name { get; set; }
        public bool IsCurrentlyRunning { get; set; }
        public Action StartAction { get; set; }
        public Action StopAction { get; set; }
        //public byte BehaviourId { get; set; }

        public RunningState Tick(BehaviourTree behaviourTree)
        {
            bool wasAllreadyRunning = IsCurrentlyRunning;
            if (!IsCurrentlyRunning)
            {
                Start(behaviourTree);
            }

            RunningState result = Execute(behaviourTree);

            if (result == RunningState.Running)
            {
                behaviourTree.SetIsCurrentlyRunning(this);
            }
            else
            {
                // If it was allready running but now it's stopped, then we'll let the BehaviourTree tick method stop it.
                // It's slightly faster this way.
                if (!wasAllreadyRunning)
                {
                    Stop(behaviourTree);
                }
            }

            return result;
        }

        public override string ToString()
        {
            if (!string.IsNullOrWhiteSpace(Name))
            {
                return Name + ": " + base.ToString();
            }
            return base.ToString();
        }

        public virtual void Start(BehaviourTree behaviourTree)
        {
            if (StartAction != null)
            {
                StartAction();
            }
        }

        public abstract RunningState Execute(BehaviourTree behaviourTree);

        // Note that with the implementation we're using, stop might be called multiple times. It's therefore very 
        // important that any "stop" is aware of this fact.
        public virtual void Stop(BehaviourTree behaviourTree)
        {
            IsCurrentlyRunning = false;
            if (StopAction != null)
            {
                StopAction();
            }
            if (_childrenToNotifyOnStop != null)
            {
                foreach (INotifyOnParentStop childToNotifyOnStop in _childrenToNotifyOnStop)
                {
                    childToNotifyOnStop.ParentWasStopped(behaviourTree, this);
                }
            }
        }

        public void WalkTree(Action<Behaviour> action)
        {
            action(this);

            IHasChildren hasChildren = this as IHasChildren;
            if (hasChildren != null)
            {
                foreach (Behaviour behaviour in hasChildren.GetChildren())
                {
                    behaviour.WalkTree(action);
                }
            }
        }

        public void Prepare(BehaviourTree behaviourTree)
        {
            SetupChildrenToNotifyOnStop();
        }

        private void SetupChildrenToNotifyOnStop()
        {
            _childrenToNotifyOnStop = null;
            IHasChildren hasChildren = this as IHasChildren;
            if (hasChildren != null)
            {
                foreach (Behaviour behaviour in hasChildren.GetChildren())
                {
                    INotifyOnParentStop childToNotifyOnStop = behaviour as INotifyOnParentStop;

                    if (childToNotifyOnStop != null)
                    {
                        if (_childrenToNotifyOnStop == null)
                        {
                            _childrenToNotifyOnStop = new List<INotifyOnParentStop>();
                        }

                        _childrenToNotifyOnStop.Add(childToNotifyOnStop);
                    }
                }
            }
        }
    }
}